package scenarios.net;

import dht.cleaners.NodeModelCleaner;
import dht.cleaners.WithoutCleaner;
import dht.identifier.identifier.Identifier;
import dht.identifier.identifier.Identifiers;
import dht.network.Network;
import dht.network.NetworkListener;
import dht.node.DHTDataModel;
import dht.node.DHTNodeModel;
import dht.node.DHTNodeRow;
import dht.node.Node;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * Created by IntelliJ IDEA.
 * User: gvennet
 * Date: 19.01.2010
 * Time: 12:31:47
 */
public class TestNode
{

	private final Identifier id;
	private final InetAddress ia;
	private final int port;
	private final ScheduledThreadPoolExecutor scheduledExecutor;
	private final Network network;
	private final DHTNodeModel nodeModel;
	private final DHTDataModel dataModel;
	private final int idLen;


	public TestNode(Identifier id, InetAddress ia, int port, long timeout,
					int maxNodes, long storageTime, NodeModelCleaner cleaner, int idLen)
	{
		if (ia == null)
		{
			try
			{
				ia = InetAddress.getLocalHost();
			}
			catch (UnknownHostException e)
			{
				e.printStackTrace();
			}
		}

		scheduledExecutor = new ScheduledThreadPoolExecutor(3);


		scheduledExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
		scheduledExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

		if (id == null)
		{
			id = Identifiers.randomIdentifier(idLen);
		}


		network = new Network(ia, port, scheduledExecutor, id, timeout, idLen);

		if (cleaner == null)
		{
			cleaner = new WithoutCleaner();
		}

		nodeModel = new DHTNodeModel(maxNodes, scheduledExecutor, cleaner);
		cleaner.setNodeModel(nodeModel);

		dataModel = new DHTDataModel(scheduledExecutor, storageTime);
		DHTDataModel getDataModel = new DHTDataModel();

		NetworkListener networkListener = new Node(network, nodeModel, dataModel, getDataModel);
		//nodeModel.addListener();
		network.addNetworkListener(networkListener);

		this.ia = network.getLocalAddress();
		this.id = id;
		this.port = network.getLocalPort();
		this.idLen = idLen;

	}

	public void startCleaning()
	{
		nodeModel.cleaning();
	}

	public void stopCleaning()
	{
		nodeModel.stopCleaning();
	}

	public void removingOlds()
	{
		dataModel.removingOlds();
	}

	public void startNetwork()
	{
		network.start();
		try
		{
			network.sendPingRequest(network.getLocalAddress(), network.getLocalPort());
		}
		catch (IOException e)
		{
			throw new RuntimeException(e);
		}
	}

	public void stopNetwork()
	{
		network.stopReceiving();
	}

	public void stopExecutor()
	{
		scheduledExecutor.shutdownNow();
	}

	public Identifier getId()
	{
		return id;
	}

	public int getPort()
	{
		return port;
	}

	public InetAddress getInetAddress()
	{
		return ia;
	}

	public DHTDataModel getDataModel()
	{
		return dataModel;
	}

	public DHTNodeModel getNodeModel()
	{
		return nodeModel;
	}

	public void addNode(InetAddress ia, int port)
	{
		try
		{
			network.sendPingRequest(ia, port);
		}
		catch (IOException e1)
		{
			throw new RuntimeException(e1);
		}
	}

	public Network getNetwork()
	{
		return network;
	}


	public void putValue(String text)
	{
		if (text != null && !text.isEmpty())
		{
			//String[] parts = text.replaceAll("[.,-:;!?]", " ").split(" ");
			//for (final String part : parts)
			{
				//if (part.length() > 3)
				{
					Identifier partId = Identifiers.createIdentifier(text, idLen);
					List<DHTNodeRow> nearestNodes = nodeModel.get(partId);
					for (int i = 0; i < 3; i++)
					{
						if (nearestNodes.size() > i)
						{
							try
							{
								network.sendPutValueRequest(nearestNodes.get(i).getInetAddress(), nearestNodes.get(i).getPort(), Identifiers.createIdentifier(text, idLen), text);
							}
							catch (IOException e1)
							{
								//do nothing
							}
						}
					}
				}
			}
		}
	}

	public void getValue(Identifier id)
	{
		List<DHTNodeRow> nearestNodes = nodeModel.get(id);
		try
		{
			network.sendGetValueRequest(nearestNodes.get(0).getInetAddress(), nearestNodes.get(0).getPort(), id);
		}
		catch (IOException e)
		{
		}

	}

}